package org.vogelsk.blue.app;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import org.vogelsk.blue.auxillary.BlueLevels;
import org.vogelsk.blue.objects.Ball;
import org.vogelsk.blue.objects.Brick;
import org.vogelsk.blue.objects.GenericCreature;
import org.vogelsk.blue.objects.Goodie;
import org.vogelsk.blue.objects.GoodieType;
import org.vogelsk.blue.objects.Paddle;


/**
 * 
 * Engine itself, responsible for enforcing game rules and updating elements/creatures' statuses
 * @author dejan.dosic
 *
 */
public class BlueSquireEngine {
	Random r = new Random(new Long(212415151));
	private boolean inGame;
	private boolean trainingMode;
	private int totalHitPoints = 0;
	private int points = 0;
	private int level = 0;
	private double height = 0;
	private double width = 0;
	private int lives = 3;
	private List<Brick> bricks;
	private List<Goodie> powerUps;
	private List<Ball> balls;
	private Paddle player;
	private boolean paused = true;
	private boolean breakerBall;
	private boolean enlarged;

	public BlueSquireEngine(int width2, int height2) {
		this.width = width2;
		this.height = height2;
	}

	public synchronized void initGame() {
		level = 0;
		setLives(3);
		setPoints(0);
		initLevel(level, false);
		setPaused(false);
	}
	
	public synchronized void initLevel(int level, boolean reload) {
		inGame = true;
		trainingMode = false;
		breakerBall = false;
		enlarged = false;
		balls = new ArrayList<Ball>();
		powerUps = new ArrayList<Goodie>();
		if (!reload) {
			totalHitPoints = 0;
			bricks = new ArrayList<Brick>();
			initBricksFromArray(BlueLevels.LEVELS[level]);
		}
		player = new Paddle((int)width/2, (int)height*5/6, 1);
		initBalls();
		balls.get(0).setAngle(-150);
		player.setBallOnPaddle(balls.get(0));

	}
	
	private void initBalls() {
		balls.add(new Ball(player.getX()+20, player.getY()-15, 8.5, false));
	}

	public void initBricks(int length, boolean isHorizontal, int position,
			int hitPoints) {
		for (int i = 0; i < length; i++) {
			if (isHorizontal) {
				getBricks().add(
						new Brick((int) (i * width/10 + 5), position, hitPoints, true));
				totalHitPoints += hitPoints;
			} else
				getBricks().add(
						new Brick(position, i * 53 + 5, hitPoints, true));
		}
	}
	
	public void initBricksFromArray(int[][] levelArray) {
		
		for (int j = 0; j < levelArray.length; j++){
			for (int i = 0; i < levelArray[j].length; i++) {
				if (levelArray[j][i] != 0){
					Brick newBrick = new Brick( (i * width/9 ), j*height/21 + 30  , levelArray[j][i], true);
					getBricks().add(newBrick);
					totalHitPoints += levelArray[j][i];
					
					//populating goodies
					int random = r.nextInt(8);
					Goodie goodie = null;
					GoodieType goodieTyp = null;
					switch (random){
					case 3: goodieTyp = GoodieType.PLASMA_BALL; break;
					case 2: goodieTyp = GoodieType.EXTRA_POINTS; break;
					case 1: goodieTyp = GoodieType.EXTRA_LIFE; break;
					case 4: goodieTyp = GoodieType.DEATH; break; //ain't exactly a "goodie"
					case 0: goodieTyp = GoodieType.SPLIT_BALLS; break;
					case 5: goodieTyp = GoodieType.FINISH_LEVEL; break;
					case 6: goodieTyp = GoodieType.ENLARGE; break;
						}
					if (goodieTyp != null) {
						goodie = new Goodie(newBrick.getX(), newBrick.getY(), goodieTyp);
						getBricks().get(getBricks().size() - 1).setContainingGoodie(goodie);
					}
				}
			}
		}
	}
	
	public void checkCollision() {
		for (Ball ball : getBalls()) {
			if (ball.isActive()) {
				checkPaddleCollision(ball);
			}

			for (Brick target : getBricks()) {
				if ((ball.getX() <= target.getX() + width/10)
						&& (ball.getX() >= target.getX() - 4)
						&& (ball.getY() <= target.getY() + height/21)
						&& (ball.getY() >= target.getY() - 4)) {
					if (target.getHitPoints() > 0) {
						// soundManager.play(SoundManager.HIT_CLIP);
						if (target.isDestructable()) {
							target.setHitPoints(target.getHitPoints() - 1);
							totalHitPoints -= 1;
							setPoints(getPoints() + 100);
							if (target.getHitPoints() == 0 && target.getContainingGoodie()!=null ){
								getPowerUps().add(target.getContainingGoodie());
								target.getContainingGoodie().setActive(true);
							}
						}
						if (!breakerBall)
						ball.incrementAngle(90);
					}
				}
			}
		}
		
		for (Goodie goo : getPowerUps()){
			checkPaddleCollision(goo);
		}
		
	}
	
	public double getInitialAngle(double x1, double y1, double x2, double y2) {
		double hipotenusis = Math.sqrt(Math.pow(y1 - y2, 2)	+ Math.pow(x1 - x2, 2));
		double cathete = y1 - y2;
		double sine = cathete / hipotenusis;
		double returnAngle = Math.toDegrees(Math.asin(sine));
		if (x1 > x2)
			returnAngle = 180 - returnAngle;
		return (-returnAngle);
	}

	public synchronized boolean checkGridCollision(
			GenericCreature movingCreature) {

		// detecting collision with grid

		boolean bottomSideHit = false;
		boolean topSideHit = false;
		boolean leftSideHit = false;
		boolean rightSideHit = false;

		// deciding action
		if (movingCreature instanceof Ball) {

			if ((movingCreature.getY() > getHeight() - 5)
					&& movingCreature.isGoingDown())
				bottomSideHit = true;
			if ((movingCreature.getY() < 0) && !movingCreature.isGoingDown())
				topSideHit = true;
			if ((movingCreature.getX() > getWidth() - 5)
					&& movingCreature.isGoingRight())
				rightSideHit = true;
			if ((movingCreature.getX() < 0) && !movingCreature.isGoingRight())
				leftSideHit = true;

			if (bottomSideHit && !trainingMode) {

				return true;
			}

			if ((rightSideHit && movingCreature.isGoingDown())
					|| (leftSideHit && !movingCreature.isGoingDown()))
				movingCreature.setAngle(-180 - movingCreature.getAngle()+ r.nextInt(10));
			else if (rightSideHit || leftSideHit)
				movingCreature.setAngle(180 - movingCreature.getAngle()+ r.nextInt(10));
			else if (topSideHit)
				movingCreature.setAngle(-movingCreature.getAngle()+ r.nextInt(10));
		}
		if (movingCreature instanceof Goodie){
			
			if ((movingCreature.getY() > getHeight() - 5))
				((Goodie) movingCreature).setActive(false);
		}

		return false;
	}

	public synchronized void checkPaddleCollision(GenericCreature movingCreature) {


		if (((movingCreature.getY() > player.getY() - 10) 
				&& (movingCreature.getY() < player.getY() + 10))
				&& (movingCreature.getX() < player.getX() + player.getWidth())
				&& (movingCreature.getX() > player.getX() - 6))

		{
			if (movingCreature instanceof Ball && movingCreature.isGoingDown()) {
				// check if paddle central part is hit
				if ((movingCreature.getX() < player.getX() + player.getWidth() - 10)
						&& (movingCreature.getX() > player.getX() + 10)) {
					movingCreature.setAngle(-movingCreature.getAngle()+ r.nextInt(10));
				} else {
					movingCreature.incrementAngle(180 + r.nextInt(6));

				}
				
				movingCreature.setSpeed(movingCreature.getSpeed()+0.1);
			}
			if (movingCreature instanceof Goodie) {

				Goodie good = (Goodie) movingCreature;
				if (good.isActive()) {
					good.setActive(false);
					if (good.getType() == GoodieType.EXTRA_POINTS) {
						points += 2000;
					}
					if (good.getType() == GoodieType.EXTRA_LIFE) {
						lives += 1;
					}
					if (good.getType() == GoodieType.ENLARGE) {
						if (!isEnlarged()){
						player.setWidth(player.getWidth()*2);
						setEnlarged(true);
						}
					}
					if (good.getType() == GoodieType.FINISH_LEVEL) {
						totalHitPoints = 0;
						inGame = false;
					}
					if (good.getType() == GoodieType.PLASMA_BALL) {
						breakerBall = true;
					}
					if (good.getType() == GoodieType.DEATH) {
						lives--;
						inGame = false;
					}
					if (good.getType() == GoodieType.SPLIT_BALLS) {
						Ball b = balls.get(0);
						balls.add(new Ball(b.getX(), b.getY(), b.getSpeed(), true,b.getAngle()+30));
						balls.add(new Ball(b.getX(), b.getY(), b.getSpeed(), true,b.getAngle()-30));
					}
				}
			}
		}

	}

	public synchronized void checkFire() {

		if (totalHitPoints == 0)
			setInGame(false);
		List <Ball> toRemove = new ArrayList<Ball>();
		for (Ball ball : getBalls()) {
			if (ball.isActive()) {
				 if (checkGridCollision(ball)){
					 toRemove.add(ball);
				 };
				ball.move();
			}
		}
		getBalls().removeAll(toRemove);
		if (getBalls().size() == 0) {
			setLives(getLives() - 1);
			setInGame(false);
		}
		for (Goodie goodie : getPowerUps()) {
			if (goodie.isActive()) {
				checkGridCollision(goodie);
				goodie.move();
			}
		}

	}

	public int getPlayerX() {
		return (int) player.getX();
	}

	public int getPlayerY() {
		return (int) player.getY();
	}

	public synchronized boolean isInGame() {
		return inGame;
	}

	public void setInGame(boolean inGame) {
		this.inGame = inGame;
	}

	public List<Brick> getBricks() {
		return bricks;
	}

	public void setBricks(List<Brick> bricks) {
		this.bricks = bricks;
	}

	public List<Ball> getBalls() {
		return balls;
	}

	public void setBalls(List<Ball> balls) {
		this.balls = balls;
	}

	public Paddle getPlayer() {
		return player;
	}

	public void setPlayer(Paddle player) {
		this.player = player;
	}

	public boolean isTrainingMode() {
		return trainingMode;
	}

	public void setTrainingMode(boolean trainingMode) {
		this.trainingMode = trainingMode;
	}

	public double getHeight() {
		return height;
	}

	public void setHeight(double height) {
		this.height = height;
	}

	public double getWidth() {
		return width;
	}

	public void setWidth(double width) {
		this.width = width;
	}

	public int getPoints() {
		return points;
	}

	public void setPoints(int points) {
		this.points = points;
	}

	public int getLives() {
		return lives;
	}

	public void setLives(int lives) {
		this.lives = lives;
	}

	public int getLevel() {
		return level;
	}

	public void setLevel(int level) {
		this.level = level;
	}

	public int getTotalHitPoints() {
		return totalHitPoints;
	}

	public void setTotalHitPoints(int totalHitPoints) {
		this.totalHitPoints = totalHitPoints;
	}

	public boolean isPaused() {

		return paused;
	}

	public void setPaused(boolean paused) {
		this.paused = paused;
	}

	public void launchBall(float x, float y) {
		if (!getBalls().get(0).isActive()) {
			getBalls().get(0).setAngle(
					getInitialAngle(getPlayer().getX(), getPlayer().getY(), x,
							y));
			getBalls().get(0).setActive(true);
			getPlayer().setBallOnPaddle(null);
		}

	}

	public void relocatePaddle(float x, float y) {
		getPlayer().setX((int) x - getPlayer().getWidth() / 2);
		if (getPlayer().getBallOnPaddle() != null) {
			getPlayer().getBallOnPaddle().setX(
					(getPlayer().getX() + getPlayer().getWidth() / 2));
		}

	}

	public List<Goodie> getPowerUps() {
		return powerUps;
	}

	public void setPowerUps(List<Goodie> powerUps) {
		this.powerUps = powerUps;
	}

	public boolean isBreakerBall() {
		return breakerBall;
	}

	public void setBreakerBall(boolean breakerBall) {
		this.breakerBall = breakerBall;
	}

	public boolean isEnlarged() {
		return enlarged;
	}

	public void setEnlarged(boolean enlarged) {
		this.enlarged = enlarged;
	}

}
